package org.fjsei.yewu.graphql;

import graphql.GraphQLError;
import graphql.schema.DataFetchingEnvironment;
import org.fjsei.yewu.exception.CommonGraphQLException;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.function.ServerRequest;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

/**手动添加一个异常转换器，
 * 【注意】不加这个的话： 就只会有一个默认的哪一个报错处理器发给前端的提示没法看清具体原因的: 都是INTERNAL ERROR。
 * 有些没打印日志的报错可能在这里捕捉到的，前端接收包也显示错误信息。
 * */
@Configuration
public class GlobalExceptionTranslator implements DataFetcherExceptionResolver {

    @Override
    public Mono<List<GraphQLError>> resolveException(Throwable exception, DataFetchingEnvironment environment) {
        List<GraphQLError> errorList = new ArrayList<>();

        if (exception instanceof GraphQLError) {
            // <CHANGE> 特殊处理CommonGraphQLException，即使它实现了GraphQLError接口
            if (exception instanceof CommonGraphQLException) {
                CommonGraphQLException commonEx = (CommonGraphQLException) exception;

                // 根据异常信息判断是否需要设置特殊状态码
                String message = commonEx.getMessage();
                if (isAuthenticationError(message)) {
                    // 设置401状态码
                    commonEx.getExtensions().put("httpStatusCode", 401);
                    commonEx.getExtensions().put("errorCode", "UNAUTHORIZED");
                } else if (isAuthorizationError(message)) {
                    // 设置403状态码
                    commonEx.getExtensions().put("httpStatusCode", 403);
                    commonEx.getExtensions().put("errorCode", "FORBIDDEN");
                }

                errorList.add(commonEx);
            } else {
                errorList.add((GraphQLError) exception);
            }
        } else {
            String invalidId = environment.getField().toString();
            String errStr = exception.getMessage();
            int httpStatusCode = 200;

            if (exception instanceof AccessDeniedException) {
                errStr = "您没有被授权不能访问";
                httpStatusCode = 403;
            }

            Throwable nestExp01 = exception.getCause();
            if (null != nestExp01 && null != nestExp01.getCause()) {
                errStr = nestExp01.getCause().getMessage();
            }

            if (!StringUtils.hasText(errStr)) {
                if (null != nestExp01) {
                    errStr = nestExp01.getMessage();
                } else {
                    errStr = exception.toString();
                }
            }

            CommonGraphQLException graphQLException = new CommonGraphQLException(errStr, invalidId);
            if (httpStatusCode != 200) {
                graphQLException.getExtensions().put("httpStatusCode", httpStatusCode);
                graphQLException.getExtensions().put("errorCode", getErrorCode(httpStatusCode));
            }

            errorList.add(graphQLException);
        }

        return Mono.just(errorList);
    }

    // <CHANGE> 添加认证错误判断方法
    private boolean isAuthenticationError(String message) {
        return message != null && (
                message.contains("没有登录") ||
                        message.contains("未登录") ||
                        message.contains("not logged in") ||
                        message.contains("authentication required")
        );
    }

    // <CHANGE> 添加授权错误判断方法
    private boolean isAuthorizationError(String message) {
        return message != null && (
                message.contains("没有权限") ||
                        message.contains("权限不足") ||
                        message.contains("access denied") ||
                        message.contains("forbidden")
        );
    }

    private String getErrorCode(int statusCode) {
        switch (statusCode) {
            case 401:
                return "UNAUTHORIZED";
            case 403:
                return "FORBIDDEN";
            default:
                return "UNKNOWN_ERROR";
        }
    }
}
